#ifndef INC_QUATERNION_H
#define INC_QUATERNION_H

#include "Vector3.h"

namespace gti
{

class matrix33;
class matrix44;

class quaternion  
{
public:

	union
	{
		struct {float x; float y; float z; float w;};
		float q[4];
	};

public:
	quaternion();
	quaternion(const float* q);
	quaternion(const quaternion& q);
	quaternion(const float X, const float Y,  const float Z, const float W);
	quaternion(const vector3f& axis, float angle);
	
	void identity();
	quaternion invert() const;
	quaternion conjugate() const;

	void set(const float X, const float Y, const float Z, const float W);
	void slerp(const quaternion& b, float t);
	void slerp(const quaternion& q2, float t, quaternion &q3) const;

	void lerp(const quaternion& b, float t);
	void lerp(const quaternion& q2, float t, quaternion &q3) const;

public:
	void setAxisAngle(const vector3f& axis, const float angle);
	void setAxisAngle(float x, float y, float z, float angle);
	void getAxisAngle(vector3f &v, float &angle) const;

	vector3f rotate(const vector3f& v) const;

	void operator*=(const vector3f& v);
	void operator *= (const quaternion &q);
	void operator += (const quaternion &q);

  friend quaternion operator + (const quaternion &q1, const quaternion& q2);
	friend quaternion operator * (const quaternion &q1, const quaternion& q2);

	friend quaternion operator * (const quaternion &q, const vector3f& v); 

	friend quaternion operator * (float f, const quaternion &q); 
    friend quaternion operator * (const quaternion &q, float f); 

    quaternion& operator -();

    
	friend bool operator==(const quaternion& q1, const quaternion& q2);
	friend bool operator!=(const quaternion& q1, const quaternion& q2);

    void operator *= (float f); 

	void computeMinimumRotation(const vector3f& rotateFrom,const vector3f& rotateTo);

	void normalize();
	float squaredLength() const;
	float length () const;
	void toMatrix(matrix33 &) const; 
	void toMatrix(matrix44 &) const;

	void toEulerAngles(vector3f &euler) const;

	float& operator[] (unsigned int i) {return q[i];}
};


float DotProduct(const quaternion &q1, const quaternion &q2);
quaternion Qlerp(const quaternion &q1,const quaternion &q2,float t);
quaternion Qslerp(const quaternion &q1,const quaternion &q2,float t);
quaternion Qsquad(const quaternion &q1,const quaternion &q2,const quaternion &a,const quaternion &b,float t);
quaternion Qsquad(const quaternion &q1,const quaternion &q2,const quaternion &a,float t);
quaternion Qspline(const quaternion &q1,const quaternion &q2,const quaternion &q3);
quaternion QslerpNoInvert(const quaternion &q1,const quaternion &q2,float t);
quaternion Qexp(const quaternion &q);
quaternion Qlog(const quaternion &q);
quaternion SimpleRotation(const vector3f &a, const vector3f &b);


void QuaternionYaw(float angle,quaternion& quat);
void QuaternionPitch(float angle,quaternion& quat);
void QuaternionRoll(float angle,quaternion& quat);
void QuaternionYawPitchRoll(float yaw, float pitch, float roll,quaternion& quat);

};//namespace gti

#endif//INC_QUATERNION_H
